package groupd

import (
	"fmt"
	"time"

	"github.com/globalsign/mgo"
	"github.com/globalsign/mgo/bson"
	"github.com/pkg/errors"
	"gogs.xiaoyuanjijiehao.com/antlinker/antcms/core/common"
	cgroupd "gogs.xiaoyuanjijiehao.com/antlinker/antcms/core/groupd"
	"gogs.xiaoyuanjijiehao.com/antlinker/antcms/core/repo/mgorepo/baser"
	"gogs.xiaoyuanjijiehao.com/antlinker/antcms/ddd"
	"gogs.xiaoyuanjijiehao.com/antlinker/antcms/ddd/repo/mgorepo"
	"gogs.xiaoyuanjijiehao.com/antlinker/antcms/log"
)

const (
	collNameGroup     = "i_group"     // 信息组集合
	collNameGroupInfo = "i_groupInfo" // 信息组信息集合
)

// CreDomainOption 创建域配置
func CreDomainOption() cgroupd.DomainOption {
	mgorepo.RegColForIndex(&collGroup{})
	mgorepo.RegColForIndex(&collGroupInfo{})
	return cgroupd.DomainOption{
		GroupRepo:        &groupRepo{},
		GroupServiceRepo: &groupRepo{},
	}

}

// groupRepo 信息仓储
type groupRepo struct {
	baser.BaseRepo
}

// 新增信息
func (r *groupRepo) AddGroup(ctx ddd.Context, group cgroupd.GroupDesc) (id string, err error) {
	uid := ctx.UID()
	now := time.Now()
	c := collGroup{
		CreTime:    now,
		Creator:    uid,
		Updater:    uid,
		UpdateTime: now,
		GroupID:    bson.NewObjectId().Hex(),
		Status:     cgroupd.GSStop,
	}
	c.FullDesc(group)
	return c.GroupID, r.DBInsert(ctx, c)
}

// 获取指定信息：根据唯一标识获取信息实体
func (r *groupRepo) GetGroup(ctx ddd.Context, groupid string) (*cgroupd.GroupAggregate, error) {
	a, err := r.GetByCollAggregate(ctx, &collGroup{GroupID: groupid})
	if err != nil {
		if err == mgo.ErrNotFound {
			return nil, nil
		}
		return nil, err
	}
	if a == nil {
		return nil, nil
	}
	return a.(*cgroupd.GroupAggregate), nil
}

// 分页获取信息列表：根据唯一标识获取下一页指定数量的信息实体集合
// total 总数量
func (r *groupRepo) QueryPage(ctx ddd.Context, sch cgroupd.GroupSearch, pi common.PageInfo) (npi common.PageResult, result []cgroupd.GroupAggregate, err error) {
	query := bson.M{}
	if sch.Org == "" && sch.Owner == "" {
		if sch.Range == 1 {
			query["org"] = ""
			query["owner"] = ""
		}
	} else {
		if sch.Owner != "" {
			query["owner"] = sch.Owner
		}
		if sch.Org != "" {
			query["org"] = sch.Org
		}
	}
	if sch.Key != "" {
		query["name"] = bson.M{"$regex": sch.Key}
	}
	if sch.Kind > cgroupd.GroupKindAll {
		query["kind"] = sch.Kind
	}
	if len(sch.Status) > 0 {
		query["status"] = bson.M{"$in": sch.Status}
	}
	if len(sch.Flags) > 0 {
		query["flags"] = bson.M{"$all": sch.Flags}
	}
	var cs []collGroup

	npi, err = r.DBQueryPage(ctx, collNameGroup, pi, "_id", query, nil, nil, &cs)
	if err != nil {
		if err == mgo.ErrNotFound {
			err = nil
			npi.End = 1
			return
		}
		err = errors.Wrapf(err, "查询组信息失败:%v", err)
		return
	}
	if len(cs) == 0 {
		npi.End = 1
		return
	}
	for _, c := range cs {
		ia := c.ToGroupAggregate()
		// 初始化聚合
		ia.Init(ia, r.AggregateRoot(), c.CollID())
		result = append(result, *ia)
	}
	return
}

// 修改信息
func (r *groupRepo) UpdateDesc(ctx ddd.Context, gid string, group cgroupd.GroupDesc) error {
	update := bson.M{
		"name":       group.Name,
		"shortName":  group.ShortName,
		"kind":       group.Kind,
		"owner":      group.Owner,
		"org":        group.Org,
		"icon":       group.Icon,
		"desc":       group.Desc,
		"updater":    ctx.UID(),
		"updateTime": time.Now(),
	}
	return r.DBUpdateCollAggregate(ctx, &collGroup{GroupID: gid}, update)
}

// 修改信息组控制参数
func (r *groupRepo) UpdateCtlParam(ctx ddd.Context, gid string, group cgroupd.GroupCtlParam) error {
	update := bson.M{
		"infoWeight": group.InfoWeight,
		"rules":      fullRules(group.Rules),
		"flags":      group.Flags,
		"updater":    ctx.UID(),
		"updateTime": time.Now(),
	}
	return r.DBUpdateCollAggregate(ctx, &collGroup{GroupID: gid}, update)
}

// 删除信息 标记删除
func (r *groupRepo) Delete(ctx ddd.Context, groupid string) error {

	if err := r.setStatusByID(ctx, groupid, cgroupd.GSDeleted); err != nil {
		return err
	}
	if err := r.DeleteCache(ctx, &collGroup{GroupID: groupid}); err != nil {
		log.Warnf("删除信息组%v缓存时失败：%v", groupid, err)
	}
	return nil
}

// 恢复删除的信息组
func (r *groupRepo) RecoverByID(ctx ddd.Context, groupid string) error {

	if err := r.setStatusByID(ctx, groupid, cgroupd.GSStop); err != nil {
		return err
	}

	return nil
}

// 停止收集信息
func (r *groupRepo) StopCollectingByID(ctx ddd.Context, groupid string) error {
	return r.setCollectingByID(ctx, groupid, cgroupd.GSStop)
}

// 开始收集信息
func (r *groupRepo) StartCollectingByID(ctx ddd.Context, groupid string) error {
	return r.setCollectingByID(ctx, groupid, cgroupd.GSNormal)

}

// 开始收集信息
func (r *groupRepo) setCollectingByID(ctx ddd.Context, groupid string, status cgroupd.GStatus) error {
	return r.DBUpdateCollAggregate(ctx, &collGroup{GroupID: groupid}, bson.M{"status": status})
}

// 开始收集信息
func (r *groupRepo) setStatusByID(ctx ddd.Context, groupid string, status cgroupd.GStatus) error {
	update := bson.M{
		"status": status,
	}
	return r.DBUpdateID(ctx, collNameGroup, groupid, update)
}

// 清空组内所有的信息
func (r *groupRepo) Empty(ctx ddd.Context, groupid string) error {
	query := bson.M{
		"groupId": groupid,
	}
	return r.DBExecSync(ctx, collNameGroupInfo, func(c *mgo.Collection) error {
		_, err := c.RemoveAll(query)
		if err != nil && err != mgo.ErrNotFound {
			return err
		}
		return nil
	})
}

// 刷新组内信息，根据权重重新排序
func (r *groupRepo) Refresh(ctx ddd.Context, groupid string, num int) error {
	// TODO :还没设计好怎么刷新
	return nil
}

// 刷新组内信息，根据权重重新排序
func (r *groupRepo) MatchGroupInfo(ctx ddd.Context, groupid string, infoid string) error {
	// 获取组规则
	// g, err := r.GetGroup(ctx, groupid)
	// if err != nil {
	// 	return err
	// }
	//g.Rules

	return nil
}

// 彻底删除指定信息
func (r *groupRepo) DestroyByID(ctx ddd.Context, groupid string) error {
	return r.DestroyByCollAggregate(ctx, &collGroup{GroupID: groupid})
}

// 按分页查询符合指定标志的组的信息
func (r *groupRepo) QueryInfoPageBySch(ctx ddd.Context, sch cgroupd.GroupSearch, pi common.PageInfo) (npi common.PageResult, result []cgroupd.GroupInfoEntity, err error) {
	query := bson.M{}
	if sch.Org == "" && sch.Owner == "" {
		if sch.Range == 1 {
			query["org"] = ""
			query["owner"] = ""
		}
	} else {
		if sch.Owner != "" {
			query["owner"] = sch.Owner
		}
		if sch.Org != "" {
			query["org"] = sch.Org
		}
	}
	if sch.Key != "" {
		query["name"] = bson.M{"$regex": sch.Key}
	}
	if sch.Kind > cgroupd.GroupKindAll {
		query["kind"] = sch.Kind
	}
	if len(sch.Status) > 0 {
		query["status"] = bson.M{"$in": sch.Status}
	}
	if len(sch.Flags) > 0 {
		query["flags"] = bson.M{"$all": sch.Flags}
	}
	var cs []collGroup

	err = r.DBQuery(ctx, collNameGroup, query, nil, nil, &cs)
	if err != nil {
		if err == mgo.ErrNotFound {
			err = nil
			return
		}
		err = errors.Wrapf(err, "查询组内信息失败")
		return
	}
	if len(cs) == 0 {
		return
	}
	var gs []string
	for _, v := range cs {
		gs = append(gs, v.GroupID)
	}

	query = bson.M{
		"groupId": bson.M{"$in": gs},
	}
	return r.queryInfoPage(ctx, query, pi)

}

// 按分页查询组内的信息
func (r *groupRepo) QueryInfoPage(ctx ddd.Context, gid string, pi common.PageInfo) (npi common.PageResult, result []cgroupd.GroupInfoEntity, err error) {
	query := bson.M{
		"groupId": gid,
	}

	return r.queryInfoPage(ctx, query, pi)
}

// 按分页查询组内的信息
func (r *groupRepo) queryInfoPage(ctx ddd.Context, query bson.M, pi common.PageInfo) (npi common.PageResult, result []cgroupd.GroupInfoEntity, err error) {
	var cs []collGroupInfo
	npi, err = r.DBQueryPage(ctx, collNameGroupInfo, pi, "seq", query, nil, []string{"-_id"}, &cs)
	if len(cs) == 0 || err != nil {
		if err == mgo.ErrNotFound {
			err = nil
		}
		return
	}
	for _, c := range cs {
		result = append(result, cgroupd.GroupInfoEntity{
			GIID: c.GIID,
			GroupInfoDesc: cgroupd.GroupInfoDesc{
				GroupID:     c.GroupID,
				Weight:      c.Weight,
				InfoID:      c.InfoID,
				CreTime:     c.CreTime,
				Seq:         c.Seq,
				Title:       c.Title,
				ColumnID:    c.ColumnID,
				ColumnName:  c.ColumnName,
				PublishTime: c.PublishTime,
			},
		})
	}
	return
}
func creSeq(weight int, id string) string {
	return fmt.Sprintf("%03d_%v", weight, id)
}

// 向组内追加信息
// groupid 指定的组
// infoid 信息编号
// weight 权重值
// 发出追加信息事件
// 如果组正处于刷新状态需要等待刷新完成后再次添加
func (r *groupRepo) AppendInfos(ctx ddd.Context, groupid string, infos []cgroupd.GroupInfoDesc) error {
	if len(infos) == 0 {
		return nil
	}

	var cs []collGroupInfo
	now := time.Now()
	for _, v := range infos {
		id := r.NextID()
		cs = append(cs, collGroupInfo{
			GIID:        groupid + "_" + v.InfoID,
			GroupID:     groupid,
			InfoID:      v.InfoID,
			Weight:      v.Weight,
			CreTime:     now,
			Seq:         creSeq(v.Weight, id),
			Title:       v.Title,
			ColumnID:    v.ColumnID,
			ColumnName:  v.ColumnName,
			PublishTime: v.PublishTime,
		})
	}
	err := r.DBExecSync(ctx, collNameGroupInfo, func(c *mgo.Collection) error {
		b := c.Bulk()
		for _, c := range cs {
			b.Upsert(bson.M{"_id": c.GIID}, bson.M{"$set": c})
		}
		if _, err := b.Run(); err != nil {
			return errors.Wrapf(err, "向组内批量追加信息失败:%v", err)
		}

		return nil
	})
	if err != nil {
		return err
	}
	return r.updateInfoCount(ctx, groupid, len(infos))
}
func (r *groupRepo) updateInfoCount(ctx ddd.Context, groupid string, num int) error {
	return r.DBExecSync(ctx, collNameGroup, func(c *mgo.Collection) error {
		err := c.Update(bson.M{"_id": groupid}, bson.M{"$inc": bson.M{"infoNum": num}})
		if err != nil && err != mgo.ErrNotFound {
			return errors.Wrapf(err, "更新组信息数量失败:%v", err)
		}
		if num < 0 {
			err := c.Update(bson.M{"_id": groupid, "infoNum": bson.M{"$lt": 0}}, bson.M{"$set": bson.M{"infoNum": 0}})
			if err != nil && err != mgo.ErrNotFound {
				return errors.Wrapf(err, "更新组信息数量为０失败:%v", err)
			}
		}
		return nil
	})
}

// func (r *groupRepo) getNextOrder(ctx ddd.Context, groupid string, num int) (o int, err error) {
// 	err = r.DBExecSync(ctx, collNameGroup, func(c *mgo.Collection) error {
// 		var result collGroup
// 		_, err := c.Find(bson.M{"_id": groupid}).Select(bson.M{"infoNum": 1}).Apply(mgo.Change{
// 			Update:    bson.M{"$inc": bson.M{"infoNum": num}},
// 			ReturnNew: true,
// 			Upsert:    true,
// 		}, &result)
// 		if err != nil {
// 			o = 0
// 		} else {
// 			o = result.InfoNum
// 		}

// 		return err
// 	})
// 	return
// }

// DeleteInfoByID 删除组内信息
// giid 指定要删除的信息的唯一标志
func (r *groupRepo) DeleteInfoByID(ctx ddd.Context, giid string) error {

	return r.DBExecSync(ctx, collNameGroupInfo, func(c *mgo.Collection) error {
		return c.RemoveId(giid)
	})

}

// DeleteInfo 删除组内信息
// infoid 指定要删除的信息
func (r *groupRepo) DeleteInfos(ctx ddd.Context, groupid string, infoids []string) error {
	defer r.updateInfoCount(ctx, groupid, -len(infoids))
	return r.DBExecSync(ctx, collNameGroupInfo, func(c *mgo.Collection) error {
		if _, err := c.RemoveAll(bson.M{"groupId": groupid, "infoId": bson.M{"$in": infoids}}); err != nil && err != mgo.ErrNotFound {
			return err
		}
		return nil
	})
}

// DeleteAllInfo 从所有组内删除指定的信息infoid
// infoid 指定要删除的信息
func (r *groupRepo) DeleteAllInfo(ctx ddd.Context, infoid string) error {

	return r.DBExecSync(ctx, collNameGroupInfo, func(c *mgo.Collection) error {
		_, err := c.RemoveAll(bson.M{"infoId": infoid})
		return err
	})
}

// 加载符合条件的信息组
// org owner flags 必须指定其中一个条件
// org 获取改组织的信息组
// owner 获取个人的信息组
// 获取指定标识的信息组，必须标识全部匹配才可以
func (r *groupRepo) LoadGroups(ctx ddd.Context, org string, owner string, flags []string) (result []cgroupd.GroupAggregate, err error) {
	query := bson.M{"org": org, "owner": owner}
	if len(flags) > 0 {
		query["flags"] = bson.M{"$all": flags}
		query["status"] = bson.M{"$gt": -1}
	}
	var cs []collGroup
	err = r.DBQuery(ctx, collNameGroup, query, nil, nil, &cs)
	if err != nil || len(cs) == 0 {

		return
	}
	for _, c := range cs {
		ia := c.ToGroupAggregate()
		// 初始化聚合
		ia.Init(ia, r.AggregateRoot(), c.CollID())
		result = append(result, *ia)
	}
	return
}

// ExistNameByDesc 判断name是否存已经存在
// org 不为空，信息组范围 组织信息组
// owner 不为空，信息组范围指定所有者的信息组
// org 和owner都为空 则平台信息组
// groupid 排除指定的信息组
// ok true 存在 false 存在
// err 不等于nil查询失败
func (r *groupRepo) ExistName(ctx ddd.Context, org, owner, groupid, name string) (ok bool, err error) {
	query := bson.M{
		"status": 0,
		"name":   name,
		"org":    org,
		"owner":  owner,
	}
	if groupid != "" {
		query["_id"] = bson.M{"$ne": groupid}
	}
	return r.DBExists(ctx, collNameGroup, query)
}

// UpdateInfoWeight 更新权重
func (r *groupRepo) UpdateInfoWeight(ctx ddd.Context, giid string, weight int) error {

	return r.DB(ctx).ExecSync(collNameGroupInfo, func(c *mgo.Collection) error {
		var cgi collGroupInfo
		if err := c.FindId(giid).One(&cgi); err != nil {
			return errors.Wrapf(err, "更新权重时查找需要更新的信息失败。")
		}
		seq := cgi.Seq
		seq = creSeq(weight, seq[4:])
		return c.UpdateId(giid, bson.M{"$set": bson.M{"weight": weight, "seq": seq}})

	})
}

// 信息编号是否存在组内
// 返回所有存在组内的id切片
func (r *groupRepo) ExistsForInfo(ctx ddd.Context, groupid string, infoids []string) ([]string, error) {
	var cis []collGroupInfo
	err := r.DBQuery(ctx, collNameGroupInfo, bson.M{
		"groupId": groupid,
		"infoId":  bson.M{"$in": infoids},
	}, bson.M{"infoId": 1}, nil, &cis)
	if err != nil {
		return nil, errors.Wrapf(err, "查询信息编号(%v)是否存在组(%v)内", infoids, groupid)
	}
	out := make([]string, 0, len(cis))
	for _, v := range cis {
		out = append(out, v.InfoID)
	}
	return out, nil
}
